﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

// TODO: 给用户一个输入框（InputBox）

namespace AVGDialogs
{
    /// <summary>
    /// AVG对话框的核心功能
    /// </summary>
    public class AVGDialog
    {

        internal List<string> backlog = new List<string>();

        internal List<AVGEvent> avgEvents = new List<AVGEvent>();

        /// <summary>
        /// 在指定的Form上初始化AVGDialog
        /// </summary>
        /// <param name="form"></param>
        public AVGDialog(Form form)
        {
            this.attachForm = form;
        }
        private readonly Form attachForm;
        /// <summary>
        /// 清空当前消息队列里的消息
        /// </summary>
        public void Clear()
        {
            avgEvents.Clear();
        }
        /// <summary>
        /// 手动向BackLog里添加一条消息
        /// </summary>
        /// <param name="msg"></param>
        public void PushBacklog(string msg)
        {
            if (backlog.Count > 300)
            {
                if (backlog.Count > 250)
                {
                    backlog.RemoveAt(0);
                }
            }
            backlog.Add(msg);
        }
        /// <summary>
        /// 清空BackLog
        /// </summary>
        public void ClearBacklog()
        {
            backlog.Clear();
        }

        internal void AddEvent(AVGEvent ev)
        {
            if (isRunning)
            {
                throw new InvalidOperationException("对话进行中时，不能修改命令。");
            }
            avgEvents.Add(ev);
        }


        /// <summary>
        /// 添加一条普通的消息。说话人，字体颜色等会继承此前的设定
        /// <br />此消息内容可以动态生成，传入的函数将会在脚本运行到此处时执行。
        /// <br />若使用了跳过功能，则这里的函数不会被执行，请注意这一点。
        /// </summary>
        /// <param name="str"></param>
        public void Text(Func<string> str)
        {
            AddEvent(new AVGShowStringEvent(str));
        }


        /// <summary>
        /// 添加一条普通的消息。说话人，字体颜色等会继承此前的设定
        /// </summary>
        /// <param name="str"></param>
        public void Text(string str)
        {
            AddEvent(new AVGShowStringEvent(str));
        }


        /// <summary>
        /// 添加一条带有说话人名字的消息。说话人名字会持续直到下一次更改。
        /// </summary>
        /// <param name="speaker"></param>
        /// <param name="str"></param>
        public void Text(string speaker, string str)
        {
            AddEvent(new AVGSetSpeakerEvent(speaker));
            AddEvent(new AVGShowStringEvent(str));
        }

        /// <summary>
        /// 设置说话人的名字。说话人的状态会持续直到下一次更改。
        /// </summary>
        /// <param name="speaker"></param>
        public void SetSpeaker(string speaker)
        {
            AddEvent(new AVGSetSpeakerEvent(speaker));
        }
        /// <summary>
        /// 清空说话人的名字，此时界面上将不会显示说话人，仅显示消息文本
        /// </summary>
        public void ClearSpeaker()
        {
            AddEvent(new AVGClearSpeakerEvent());
        }

        /// <summary>
        /// 移动文本窗口，采用百分比坐标，0代表最左/上边，1代表最右/下边
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public void MovePosition(double x, double y)
        {
            AddEvent(new AVGMoveDialogPositionEvent(x, y));
        }

        /// <summary>
        /// 重置文本窗口到默认位置
        /// </summary>
        public void ResetPosition()
        {
            AddEvent(new AVGMoveDialogPositionEvent());
        }

        /// <summary>
        /// 添加选项，选项必须要有1-5个。选择后，可以通过 <see cref="GetLastSelection"/> 获取上一次选择的输入。
        /// </summary>
        /// <param name="selections"></param>
        public void AddSelection(params string[] selections)
        {
            AddEvent(new AVGShowSelectionEvent(selections));
        }


        /// <summary>
        /// 添加选项，选项必须要有1-5个。选择后，可以通过 <see cref="GetLastSelection"/> 获取上一次选择的输入。
        /// <br />此方法允许动态生成选项，传入的Func将会在脚本执行到此处时调用。选项必须有1-5个
        /// </summary>
        /// <param name="getSelectionFunc"></param>
        public void AddSelection(Func<string[]> getSelectionFunc)
        {
            AddEvent(new AVGShowSelectionEvent(getSelectionFunc));
        }


        internal int lastSelection = -1;
        /// <summary>
        /// 获取上一个选项的选择。<br />
        /// 单个对话只能出现一次选项，否则后出现的选项会覆盖前面出现的选项。除非前面的是单选题。
        /// 返回1-5，分别代表每个选项
        /// 建议把选项放在末尾。
        /// </summary>
        /// <returns></returns>
        public int GetLastSelection()
        {
            return lastSelection;
        }

        /// <summary>
        /// 标记接下来一段剧情可以跳过。可以填写跳过剧情时，要显示的剧情更改。
        /// <br />
        /// 跳过区域将会以<see cref="EndSkipSection"/>结束，或者下一个新的<see cref="BeginSkipSection(string)"/>，或者遇到当前对话结尾，或者遇到第一个选项后。
        /// </summary>
        /// <param name="storySummary">当点击跳过时，显示的梗概</param>
        public void BeginSkipSection(string storySummary)
        {
            AddEvent(new AVGBeginSkipSection(storySummary));
        }

        /// <summary>
        /// 标记跳过停止的区域，当点击跳过后，会停在当前标记的下一条对话
        /// </summary>
        public void EndSkipSection()
        {
            AddEvent(new AVGEndSkipSection());
        }

        bool isRunning = false;

        /// <summary>
        /// 开始显示此前添加的对话。该方法会阻塞直到对话播放完毕。
        /// <br />因为使用的是<see cref="Form.ShowDialog(IWin32Window)"/>方法造成的阻塞，因此不会导致父窗口无响应。
        /// 而且，因为做了特殊的适配，在此期间您仍然可以移动父窗口
        /// </summary>
        public bool Begin()
        {
            isRunning = true;
            var result = new AVGDialogWindow(this).ShowImpl(attachForm) == DialogResult.OK;
            isRunning = false;
            return result;
        }

        /// <summary>
        /// 添加延时效果。
        /// </summary>
        /// <param name="ms"></param>
        public void Delay(int ms) => AddEvent(new AVGDelayEvent(ms));

        /// <summary>
        /// 添加一个Label。可以在后续的GoTo和GoToIf里
        /// </summary>
        /// <param name="name"></param>
        public void Label(string name) => AddEvent(new AVGLabelEvent(name));

        /// <summary>
        /// 添加一个隐藏消息的功能
        /// </summary>
        public void HideMessage() => AddEvent(new AVGHideMessageBox());

        /// <summary>
        /// 插入一段C#代码用于执行（当脚本运行到此处时）<br />
        /// 若代码比较长，可能会导致界面卡住，此时可以使用多线程。<br />
        /// 使用<see cref="Label(string)"/>，<see cref="Delay(int)"/>和<see cref="GotoIf(string, Func{bool})"/>，可以实现等待多线程任务完成后再继续执行脚本。<br />
        /// 注：在脚本运行期间，无法再动态修改脚本内容。因此请一次性编写好所有的分支情况，而不是根据分支的清空添加新的内容。
        /// </summary>
        /// <param name="code"></param>
        public void Exec(Action code) => AddEvent(new AVGExec(code));

        /// <summary>
        /// 无条件跳转到指定的Label处。<br />
        /// 若脚本内部发生死循环，例如连续运行了超过2048条非交互式指令，则会抛出异常。请尽量将复杂计算塞进<see cref="Exec(Action)"/>里
        /// </summary>
        /// <param name="label"></param>
        public void Goto(string label) => AddEvent(new AVGGotoEvent(label));
        /// <summary>
        /// 当脚本运行到此处时，会执行判断，若条件满足，则会发生跳转<br />
        /// 若脚本内部发生死循环，例如连续运行了超过2048条非交互式指令，则会抛出异常。请尽量将复杂计算塞进<see cref="Exec(Action)"/>里
        /// </summary>
        /// <param name="label"></param>
        /// <param name="predicate"></param>
        public void GotoIf(string label,Func<bool> predicate) => AddEvent(new AVGGotoIfEvent(label,predicate));
        /// <summary>
        /// 若上一次选项选中了某个项目，则跳转。
        /// </summary>
        /// <param name="label"></param>
        /// <param name="which"></param>
        public void GotoIfSelection(string label,int which) => AddEvent(new AVGGotoIfEvent(label,()=>GetLastSelection() == which));

        /// <summary>
        /// 晃动屏幕（通常是指晃动所在的窗口（若此时窗口最大化则该指令无效））
        /// </summary>
        /// <param name="mode">模式</param>
        /// <param name="times">次数（通常建议填写8即可）</param>
        /// <param name="amplitude">幅度（像素，建议填写10即可，可以自行探索不同值的效果）</param>
        public void StrikeScreen(AVGStrikeScreenMode mode, int times, int amplitude) => AddEvent(new AVGStrikeScreen(mode, times, amplitude));

        /// <summary>
        /// 设置说话者名字的字体颜色。设置后持续有效。
        /// </summary>
        /// <param name="color"></param>
        public void SetSpeakerColor(Color color) => AddEvent(new AVGSetSpeakerColor(color));
        /// <summary>
        /// 重置说话者名字的字体颜色为默认的颜色
        /// </summary>
        public void ResetSpeakerColor() => AddEvent(new AVGSetSpeakerColor(Color.FromArgb(255, 255, 192)));
        /// <summary>
        /// 设置消息文本的字体颜色，设置后持续有效。
        /// </summary>
        /// <param name="color"></param>
        public void SetTextColor(Color color) => AddEvent(new AVGSetTextColor(color)); 

        /// <summary>
        /// 重置消息文本的字体颜色
        /// </summary>
        public void ResetTextColor() => AddEvent(new AVGSetTextColor(Color.White)); 

        /// <summary>
        /// 设置消息文本的字体大小，持续有效
        /// </summary>
        /// <param name="size"></param>
        public void SetTextSize(AVGTextSizes size) => AddEvent(new AVGSetTextSize(size));

        /// <summary>
        /// 设置某个选项的颜色，将会在下一次弹出选项时生效，且只会生效一次。
        /// </summary>
        /// <param name="which"></param>
        /// <param name="color"></param>
        public void SetSelectionColor(int which,Color color) => AddEvent(new AVGSetSelectionColor(which,color));



        internal abstract class AVGEvent { }
        internal interface ISkipableEvent { }

        internal class AVGShowStringEvent : AVGEvent,ISkipableEvent
        {
            private string _message;
            public string Message {
                get
                {
                    return getMessageFunc == null ? _message : getMessageFunc();
                }
            }
            public Func<string> getMessageFunc;
            public AVGShowStringEvent(string message)
            {
                _message = message;
            }

            public AVGShowStringEvent(Func<string> getMessageFunc) { 
                this.getMessageFunc = getMessageFunc;
            }
        }

        internal class AVGSetSpeakerEvent : AVGEvent, ISkipableEvent
        {
            public string SpeakerName;

            public AVGSetSpeakerEvent(string speakerName)
            {
                SpeakerName = speakerName;
            }
        }

        internal class AVGClearSpeakerEvent : AVGEvent, ISkipableEvent
        {

        }

        internal class AVGMoveDialogPositionEvent : AVGEvent, ISkipableEvent
        {
            public double X, Y;

            public AVGMoveDialogPositionEvent(double x, double y)
            {
                X = x;
                Y = y;
            }
            public AVGMoveDialogPositionEvent()
            {
                X = 0.5;
                Y = 0.9;
            }
        }

        internal class AVGShowSelectionEvent : AVGEvent
        {
            private List<string> evaledSelections;
            private Func<string[]> getSelectionFunc;
            public List<string> Selections {
                get
                {
                    if (getSelectionFunc != null)
                    {
                        var selections = getSelectionFunc(); 
                        if (selections == null || selections.Length == 0)
                        {
                            throw new ArgumentNullException(nameof(selections));
                        }
                        if (selections.Length > 5)
                        {
                            throw new ArgumentException("Selections should at most be 5.");
                        }
                        evaledSelections = selections.ToList();
                    }
                    return evaledSelections;
                }
            }

            public AVGShowSelectionEvent(Func<string[]> getSelectionFunc)
            {
                this.getSelectionFunc = getSelectionFunc;
            }

            public AVGShowSelectionEvent(params string[] selections)
            {
                if (selections == null || selections.Length == 0)
                {
                    throw new ArgumentNullException(nameof(selections));
                }
                if (selections.Length > 5)
                {
                    throw new ArgumentException("Selections should at most be 5.");
                }

                evaledSelections = selections.ToList();
            }
        }



        internal class AVGBeginSkipSection : AVGEvent
        {
            public string StorySummary;

            public AVGBeginSkipSection(string storySummary)
            {
                StorySummary = storySummary;
            }
        }

        internal class AVGEndSkipSection : AVGEvent
        {

        }

        internal class AVGDelayEvent : AVGEvent, ISkipableEvent {
            public int delayMs;

            public AVGDelayEvent(int delayMs)
            {
                this.delayMs = delayMs;
            }
        }

        internal class AVGLabelEvent : AVGEvent, ISkipableEvent {
            public string labelName;

            public AVGLabelEvent(string labelName)
            {
                this.labelName = labelName;
            }
        }

        internal class AVGHideMessageBox : AVGEvent,ISkipableEvent
        {

        }
        internal class AVGExec : AVGEvent
        {
            public Action exec;
            public AVGExec(Action exec)
            {
                this.exec = exec;
            }
        }

        /// <summary>
        /// 晃动屏幕时的方向
        /// </summary>
        public enum AVGStrikeScreenMode
        {
            /// <summary>
            /// 左右晃动
            /// </summary>
            Horizontal,
            /// <summary>
            /// 上下晃动
            /// </summary>
            Vertical,
            /// <summary>
            /// 均有
            /// </summary>
            Both
        }

        internal class AVGStrikeScreen : AVGEvent, ISkipableEvent
        {
            public AVGStrikeScreenMode mode;
            public int times;
            public int amplitude;

            public AVGStrikeScreen(AVGStrikeScreenMode mode, int times, int amplitude)
            {
                this.mode = mode;
                this.times = times;
                this.amplitude = amplitude;
            }
        }
        internal class AVGSetSpeakerColor : AVGEvent, ISkipableEvent
        {
            public Color color;

            public AVGSetSpeakerColor(Color color)
            {
                this.color = color;
            }
        }
        internal class AVGSetTextColor : AVGEvent, ISkipableEvent
        {
            public Color color;

            public AVGSetTextColor(Color color)
            {
                this.color = color;
            }
        }
        /// <summary>
        /// 字体大小
        /// </summary>
        public enum AVGTextSizes
        {
            /// <summary>
            /// 较小的字体，不影响可读性，对话框大约可以显示出5行左右
            /// </summary>
            Small,
            /// <summary>
            /// 默认的字体大小，对话框可以显示3行左右
            /// </summary>
            Normal,
            /// <summary>
            /// 放大且加粗的字体，只能显示一行，在3-5个字左右
            /// </summary>
            Large
        }
        internal class AVGSetTextSize : AVGEvent, ISkipableEvent
        {
            public AVGTextSizes size;

            public AVGSetTextSize(AVGTextSizes size)
            {
                this.size = size;
            }
        }

        internal class AVGGotoEvent : AVGEvent {
            public string targetLabel;

            public AVGGotoEvent(string targetLabel)
            {
                this.targetLabel = targetLabel;
            }
        }
        internal class AVGGotoIfEvent : AVGEvent
        {
            public string targetLabel;
            public Func<bool> predicate;

            public AVGGotoIfEvent(string targetLabel, Func<bool> predicate)
            {
                this.targetLabel = targetLabel;
                this.predicate = predicate;
            }
        }

        internal class AVGSetSelectionColor : AVGEvent, ISkipableEvent
        {
            public int which;
            public Color color;

            public AVGSetSelectionColor(int which, Color color)
            {
                this.which = which;
                this.color = color;
            }
        }

    }
}
